﻿/* 2009 by Mario Meir-Huber
 * mario@meirhuber.de
 * http://www.meirhuber.de
 */
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Diagnostics;

namespace ZoomPanel
{
    /// <summary>
    /// Zoompanel
    /// </summary>
    public class ZoomPanel : Panel
    {
        private Orientation _Orientation; //stores the Orientation

        private Size _availableSize;    //indicates the available Size

        private int OldZIndex = -1; //stores z index on zooming

        private double FullWidth = 0.0; //indicates full width of all Elements
        private double FullHeight = 0.0;    //indicates full heiht of all Elements

        /// <summary>
        /// Gets the Panels Orientation or Sets it
        /// </summary>
        public Orientation Orientation 
        { 
            get
            {
                return _Orientation;
            }
            set
            {
                _Orientation = value;
                UpdatePanel();
            }
        }

        /// <summary>
        /// Updates the View of the Panel
        /// </summary>
        private void UpdatePanel()
        {
            Rect Position;

            double YPosition = 0.0;
            double XPosition = 0.0;

            Point TransformOrigin = new Point(0.5, 0);

            //it is a horizontal layout
            if (Orientation == Orientation.HorizontalBottom || Orientation == Orientation.HorizontalCenter
                || Orientation == Orientation.HorizontalTop)
            {
                XPosition = (_availableSize.Width - FullWidth) / 2;

                if (XPosition < 0.0) throw new Exception("Zoompanel contains too much Elements. Remove some elements or resize the Panel");

                YPosition = Math.Round(YPosition, 0);

                for(int i=0; i<Children.Count; i++)
                {
                    if (Orientation == Orientation.HorizontalBottom)
                    {
                        YPosition = _availableSize.Height - Children[i].DesiredSize.Height;
                        TransformOrigin = new Point(0.5, 1);
                    }
                    else if (Orientation == Orientation.HorizontalCenter)
                    {
                        YPosition = (_availableSize.Height - Children[i].DesiredSize.Height) / 2;
                        TransformOrigin = new Point(0.5, 0.5);
                    }

                    Position = new Rect();

                    Position.Width = Children[i].DesiredSize.Width;
                    Position.Height = Children[i].DesiredSize.Height;
                    
                    Position.Y = YPosition;
                    Position.X = XPosition;

                    XPosition += Position.Width;

                    Children[i].Arrange(Position);

                    Children[i].RenderTransform = new ScaleTransform();

                    Children[i].MouseEnter -= OnElementEnter;
                    Children[i].MouseEnter += OnElementEnter;

                    Children[i].MouseLeave -= OnElementLeave;
                    Children[i].MouseLeave += OnElementLeave;

                    Children[i].RenderTransformOrigin = TransformOrigin;
                }
            }//it is a vertical layout
            else
            {
                YPosition = (_availableSize.Height - FullHeight) / 2;

                if (YPosition < 0.0) throw new Exception("Zoompanel contains too much Elements. Remove some elements or resize the Panel");

                for (int i = 0; i < Children.Count; i++)
                {
                    Position = new Rect();

                    if (Orientation == Orientation.VerticalLeft)
                    {
                        XPosition = 0;
                        TransformOrigin = new Point(0, 0.5);
                    }
                    else if (Orientation == Orientation.VerticalCenter)
                    {
                        XPosition = (_availableSize.Width - Children[i].DesiredSize.Width)  / 2;
                        TransformOrigin = new Point(0.5, 0.5);
                    }
                    else if (Orientation == Orientation.VerticalRight)
                    {
                        XPosition = _availableSize.Width - Children[i].DesiredSize.Width;
                        TransformOrigin = new Point(1.0, 0.5);
                    }

                    Position.Width = Children[i].DesiredSize.Width;

                    Position.Height = Children[i].DesiredSize.Height;

                    Position.X = XPosition;
                    Position.Y = YPosition;

                    Children[i].MouseEnter -= OnElementEnter;
                    Children[i].MouseEnter += OnElementEnter;

                    Children[i].MouseLeave -= OnElementLeave;
                    Children[i].MouseLeave += OnElementLeave;
                    
                    Children[i].RenderTransformOrigin = TransformOrigin;
                    Children[i].RenderTransform = new ScaleTransform();
                    Children[i].Arrange(Position);

                    YPosition += Position.Height;
                }
            }
        }

        /// <summary>
        /// Occurs when the mouse cursor left the object
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnElementLeave(object sender, MouseEventArgs e)
        {
            UIElement s = sender as UIElement;

            s.SetValue(Canvas.ZIndexProperty, 98);
            Storyboard st = InputStoryBoard(s, 0, "(UIElement.RenderTransform).(ScaleTransform.ScaleX)", 1.5, 1.0);
            st.Begin();
            InputStoryBoard(s, 0, "(UIElement.RenderTransform).(ScaleTransform.ScaleY)", 1.5, 1.0).Begin();

            st.Completed += (sender2, e2) =>
                {
                    s.SetValue(Canvas.ZIndexProperty, OldZIndex);
                };
        }

        /// <summary>
        /// Generates a new Storyboard with the given parameters
        /// </summary>
        /// <param name="sender">UIElement to apply the Storyboard for</param>
        /// <param name="count">Delay for Storyboard</param>
        /// <param name="Property">Property to animate</param>
        /// <param name="from">startvalue</param>
        /// <param name="to">final value</param>
        /// <returns>Storyboard</returns>
        private Storyboard InputStoryBoard(UIElement sender, int count,
                                            string Property, double from, double to)
        {
            Storyboard retval = new Storyboard();
            retval.BeginTime = TimeSpan.FromMilliseconds(100 * count);

            DoubleAnimation dao = new DoubleAnimation();
            Storyboard.SetTarget(dao, sender);
            Storyboard.SetTargetProperty(dao, new PropertyPath(Property));
            dao.From = from;
            dao.To = to;
            dao.Duration = TimeSpan.FromMilliseconds(200);
            retval.Children.Add(dao);

            return retval;
        }

        /// <summary>
        /// Occurs when the mouse is over an Element
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void OnElementEnter(object sender, MouseEventArgs e)
        {
            UIElement s = sender as UIElement;

            OldZIndex = (int)s.GetValue(Canvas.ZIndexProperty);
            s.SetValue(Canvas.ZIndexProperty, 99);

            InputStoryBoard(s, 0, "(UIElement.RenderTransform).(ScaleTransform.ScaleX)", 1.0, 1.5).Begin();
            InputStoryBoard(s, 0, "(UIElement.RenderTransform).(ScaleTransform.ScaleY)", 1.0, 1.5).Begin();
        }

        /// <summary>
        /// Occurs when new Items are added or the Panel gets resized
        /// </summary>
        /// <param name="finalSize"></param>
        /// <returns></returns>
        protected override Size ArrangeOverride(Size finalSize)
        {
            UpdatePanel();

            return base.ArrangeOverride(finalSize);
        }

        /// <summary>
        /// Occurs when new Items are added or the Panel gets resized
        /// </summary>
        /// <param name="availableSize"></param>
        /// <returns></returns>
        protected override Size MeasureOverride(Size availableSize)
        {
            _availableSize = availableSize;

            foreach (UIElement child in Children)
            {
                child.Measure(availableSize);

                FullWidth += child.DesiredSize.Width;
                FullHeight += child.DesiredSize.Height;
            }

            return base.MeasureOverride(availableSize);
        }
    }
}
